package com.yuyou.fn.timer.task;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.wens.mybatisplus.examples.Example;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHander;
import com.xxl.job.core.log.XxlJobLogger;
import com.yuyou.fn.common.service.ISiteConfigService;
import com.yuyou.fn.common.util.BeanUtils;
import com.yuyou.fn.common.util.BigDecimals;
import com.yuyou.fn.common.util.TimeUtils;
import com.yuyou.fn.common.util.Utils;
import com.yuyou.fn.educational.entity.*;
import com.yuyou.fn.educational.service.*;
import com.yuyou.fn.platform.entity.BusTeam;
import com.yuyou.fn.platform.service.IBusTeamService;
import com.yuyou.fn.report.entity.CostOfGradeSubject;
import com.yuyou.fn.report.entity.CostOfProduct;
import com.yuyou.fn.report.entity.RealIncomeOfClass;
import com.yuyou.fn.report.service.ICostOfGradeSubjectService;
import com.yuyou.fn.report.service.ICostOfProductService;
import com.yuyou.fn.report.service.IRealIncomeOfClassService;
import com.yuyou.fn.settlement.constant.FeeCategoryEnum;
import com.yuyou.fn.settlement.constant.Subjects;
import com.yuyou.fn.settlement.entity.ChangeRecord;
import com.yuyou.fn.settlement.service.IChangeRecordService;
import com.yuyou.fn.settlement.service.ITradePropService;
import com.yuyou.fn.settlement.vo.FeeCategoryTreeMapVo;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

@JobHander(value="CostCountTask")
@Component
public class CostCountTask extends IJobHandler {

    @Resource
    private ITradePropService tradePropService;

    @Resource
    private IStudentService studentService;

    @Resource
    private ICourseClassService courseClassService;

    @Resource
    private ICourseTemplateService courseTemplateService;

    @Resource
    private IRealIncomeOfClassService realIncomeOfClassService;

    @Resource
    private IChangeRecordService changeRecordService;

    @Resource
    private ICostOfProductService costOfProductService;

    @Resource
    private ICostOfGradeSubjectService costOfGradeSubjectService;

    @Resource
    private IRoomPriceService roomPriceService;

    @Resource
    private IClassLectureService classLectureService;

    @Resource
    private ITeacherService teacherService;

    @Resource
    private IBusTeamService busTeamService;

    @Resource
    private IPeriodService periodService;

    @Resource
    private IStudentRegService studentRegService ;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ISiteConfigService siteConfigService;

    private Map<Long, FeeCategoryTreeMapVo> feeCategoryId2FeeCategoryOfProduct = new HashMap<>();
    private Map<Long, FeeCategoryTreeMapVo> feeCategoryId2FeeCategoryOfGradeSubject = new HashMap<>();

    @PostConstruct
    public void init() {
        foreachFeeCategory(JSONArray.parseArray(siteConfigService.getConfigValueFromCache("product_feeCategory"), FeeCategoryTreeMapVo.class), feeCategoryId2FeeCategoryOfProduct);
        foreachFeeCategory(JSONArray.parseArray(siteConfigService.getConfigValueFromCache("grade_subject_feeCategory"), FeeCategoryTreeMapVo.class), feeCategoryId2FeeCategoryOfGradeSubject);
    }

    private void foreachFeeCategory(List<FeeCategoryTreeMapVo> list, Map<Long, FeeCategoryTreeMapVo> feeCategoryId2FeeCategory) {
        for (FeeCategoryTreeMapVo vo : list) {
            feeCategoryId2FeeCategory.put(vo.getFeeCategoryId(), vo);
            if (vo.getChildren() != null) {
                foreachFeeCategory(vo.getChildren(), feeCategoryId2FeeCategory);
            }
        }
    }

    @Override
    public ReturnT<String> execute(String... params) throws Exception {
        boolean hasFail = false;
        /*先处理课程收入，某些成本需根据课程讲次计算*/

        Map<String, String> classIncomeMap = redisTemplate.opsForHash().entries("oneDayClassIncome");
        for (Map.Entry<String, String> entry : classIncomeMap.entrySet()) {
            String changeRecordIds = entry.getValue();
            String key = entry.getKey();

            List<ChangeRecord> changeRecordList = changeRecordService.findByIds(Utils.toLongIds(changeRecordIds));
            try {
                for (ChangeRecord changeRecord : changeRecordList) {
                    buildRealIncomeOfCourseClass(changeRecord);
                }
                redisTemplate.opsForHash().delete("oneDayClassIncome", key);
            } catch (Exception e) {
                hasFail = true;
                XxlJobLogger.log("处理课程收入失败:key={0},error={1}", key, Utils.getExceptionStackTrace(e));
            }
        }


        Map<String, String> map = redisTemplate.opsForHash().entries("oneDayRecordChange");

        for (Map.Entry<String, String> entry : map.entrySet()) {
            Long changeRecordId = Long.parseLong(entry.getValue());
            String key = entry.getKey();
            try {
                ChangeRecord changeRecord = changeRecordService.findById(changeRecordId);
                if (changeRecord != null) {
                    this.handle(changeRecord);
                }
                redisTemplate.opsForHash().delete("oneDayRecordChange", key);
            } catch (Exception e) {
                hasFail = true;
                XxlJobLogger.log("计算成本失败:key={0},error={1}", key, Utils.getExceptionStackTrace(e));
            }
        }

        return hasFail ?  ReturnT.FAIL : ReturnT.SUCCESS;
    }

    private void handle(ChangeRecord changeRecord) {

        Long mainAccountId = changeRecord.getMainAccountId();
        Date date  = changeRecord.getEffectTime();
        calProductCost(mainAccountId, date );
        calGradeSubjectCost(mainAccountId, date);
    }


    private void calGradeSubjectCost(Long mainAccountId, Date time) {

        List<String> gradeSubjectIds = courseClassService.howManySubjectIdGradeIdForBusTeam(mainAccountId);
        if (gradeSubjectIds == null || gradeSubjectIds.size() == 0) {
            return;
        }

        //查找这个业务组这一天发生的成本
        Calendar startOfDay = Calendar.getInstance();
        startOfDay.setTime(time);
        startOfDay.set(Calendar.HOUR, 0);
        startOfDay.set(Calendar.MINUTE, 0);
        startOfDay.set(Calendar.SECOND, 0);
        Calendar endOfDay = Calendar.getInstance();
        endOfDay.setTime(time);
        endOfDay.set(Calendar.HOUR, 23);
        endOfDay.set(Calendar.MINUTE, 59);
        endOfDay.set(Calendar.SECOND, 59);
        /*去除租金数据*/
        List<Long> feeCategoryIds  = new ArrayList<>(feeCategoryId2FeeCategoryOfProduct.keySet());
        feeCategoryIds.remove(FeeCategoryEnum.LEAVE.id);
        Example<ChangeRecord> changeRecordExample = ChangeRecord.createExample();
        changeRecordExample.createCriteria().andEqualTo("mainAccountId", mainAccountId)
                .andEqualTo("subject", Subjects.COST)
                .andEqualTo("direction", 0)
                .andIn("categoryId", feeCategoryIds)
                .andBetween("effectTime" , startOfDay.getTime() , endOfDay.getTime());


        List<ChangeRecord> changeRecords = changeRecordService.findListByExample(changeRecordExample);

        if (changeRecords.isEmpty()) {
            return;
        }

        //课酬成本
        Collection<ChangeRecord> changeRecordOfSalaryOfCourse = Collections2.filter(changeRecords, new Predicate<ChangeRecord>() {
            @Override
            public boolean apply(ChangeRecord input) {
                return input.getCategoryId().equals(FeeCategoryEnum.SALARY_OF_COURSE.id);
            }
        });


        //其他成本
        List<ChangeRecord> changeRecordOfOther = new ArrayList<>(Collections2.filter(changeRecords, new Predicate<ChangeRecord>() {
            @Override
            public boolean apply(ChangeRecord input) {
                return !input.getCategoryId().equals(FeeCategoryEnum.SALARY_OF_COURSE.id);
            }
        }));

        //清除今天成本,再重新算


        clearCostOfGradeSubject(mainAccountId, startOfDay, endOfDay);
        List<CostOfGradeSubject> costOfGradeSubjects = new ArrayList<>(changeRecordOfSalaryOfCourse.size());

        costOfGradeSubjects.addAll(buildSalaryCostOfGradeSubject(mainAccountId, changeRecordOfSalaryOfCourse));
        costOfGradeSubjects.addAll(buildOtherCostOfGradeSubject(mainAccountId, time, gradeSubjectIds, changeRecordOfOther , startOfDay.getTime() , endOfDay.getTime()));

        if (!costOfGradeSubjects.isEmpty()) {
            costOfGradeSubjectService.insertBatch(costOfGradeSubjects);
        }
    }

    private List<CostOfGradeSubject> buildOtherCostOfNowGradeSubject(Long mainAccountId, Date time, List<ChangeRecord> changeRecordOfOther ) {
        BusTeam busTeam=busTeamService.findById(mainAccountId);
        if (busTeam==null){
            return Collections.EMPTY_LIST;
        }
        Period period=periodService.findCurrentPeriod(busTeam.getAreaTeamId());
        if (period==null){
            return Collections.EMPTY_LIST;
        }
        List<String> gradeSubjectIds = courseClassService.howManyNowSubjectIdGradeIdForBusTeam(mainAccountId,Arrays.asList(0,1,3),period.getPeriodId());
        if (gradeSubjectIds==null || gradeSubjectIds.size()<=0){
            return Collections.EMPTY_LIST;
        }
        Map<Long, List<ChangeRecord>> categoryId2changeRecordOfOther = BeanUtils.toGroup(changeRecordOfOther, "categoryId");
        List<CostOfGradeSubject> costOfGradeSubjects = new ArrayList<>(gradeSubjectIds.size());
        for (Map.Entry<Long, List<ChangeRecord>> changeRecordOfCost : categoryId2changeRecordOfOther.entrySet()) {

            Long feeCategoryId = changeRecordOfCost.getKey();

            BigDecimal totalCost = BigDecimal.ZERO;
            for (ChangeRecord changeRecord : changeRecordOfCost.getValue()) {
                totalCost = totalCost.add(changeRecord.getAmount());
            }
            for (String gradeSubjectIdString: gradeSubjectIds){
                String[] gradeSubjectId = gradeSubjectIdString.split("#");
                Long gradeId = Long.valueOf(gradeSubjectId[0]);
                Long subjectId = Long.valueOf(gradeSubjectId[1]);


                CostOfGradeSubject costOfGradeSubject = new CostOfGradeSubject();
                costOfGradeSubject.setAmount(BigDecimals.calProportion(totalCost, new BigDecimal(1), new BigDecimal(gradeSubjectIds.size())));
                costOfGradeSubject.setCreateTime(TimeUtils.now());
                costOfGradeSubject.setUpdateTime(TimeUtils.now());
                costOfGradeSubject.setEffectTime(time);
                costOfGradeSubject.setFeeCategoryId(feeCategoryId);
                costOfGradeSubject.setFeeName(feeCategoryId2FeeCategoryOfGradeSubject.get(feeCategoryId).getName());
                costOfGradeSubject.setMainAccountId(mainAccountId);
                costOfGradeSubject.setGradeId(gradeId);
                costOfGradeSubject.setSubjectId(subjectId);
                costOfGradeSubjects.add(costOfGradeSubject);
            }

        }
        return costOfGradeSubjects;
    }



    private List<CostOfGradeSubject> buildOtherCostOfGradeSubject(Long mainAccountId, Date time, List<String> gradeSubjectIds, List<ChangeRecord> changeRecordOfOther,Date start , Date end ) {
        List<RealIncomeOfClass> realIncomeOfClasses = realIncomeOfClassService.findRealIncomeOfClassByBusTeamIdGradeSubjectIds(mainAccountId, gradeSubjectIds,start,end );
        if (realIncomeOfClasses.isEmpty()) {
            return this.buildOtherCostOfNowGradeSubject(mainAccountId,time,changeRecordOfOther);
        }
        List<CostOfGradeSubject> costOfGradeSubjects = new ArrayList<>(realIncomeOfClasses.size() * 2);
        Map<String, Integer> gradeSubjectId2HappenedLectureAmount = new HashMap<>();

        for (RealIncomeOfClass realIncomeOfClass : realIncomeOfClasses) {
            String gradeSubjectId = realIncomeOfClass.getGradeId() + "#" + realIncomeOfClass.getSubjectId();
            Integer happenedLectureAmount = gradeSubjectId2HappenedLectureAmount.get(gradeSubjectId);
            if (happenedLectureAmount == null) {
                happenedLectureAmount = 0;
            }
            gradeSubjectId2HappenedLectureAmount.put(gradeSubjectId, happenedLectureAmount + 1);
        }

        Map<Long, List<ChangeRecord>> categoryId2changeRecordOfOther = BeanUtils.toGroup(changeRecordOfOther, "categoryId");

        for (Map.Entry<Long, List<ChangeRecord>> changeRecordOfCost : categoryId2changeRecordOfOther.entrySet()) {

            Long feeCategoryId = changeRecordOfCost.getKey();

            BigDecimal totalCost = BigDecimal.ZERO;
            for (ChangeRecord changeRecord : changeRecordOfCost.getValue()) {
                totalCost = totalCost.add(changeRecord.getAmount());
            }

            int totalHappenedLectureAmount = realIncomeOfClasses.size();
            for (Map.Entry<String, Integer> entry : gradeSubjectId2HappenedLectureAmount.entrySet()) {
                String[] gradeSubjectId = entry.getKey().split("#");
                Long gradeId = Long.valueOf(gradeSubjectId[0]);
                Long subjectId = Long.valueOf(gradeSubjectId[1]);

                int happenedLectureAmount = entry.getValue();

                CostOfGradeSubject costOfGradeSubject = new CostOfGradeSubject();
                costOfGradeSubject.setAmount(BigDecimals.calProportion(totalCost, new BigDecimal(happenedLectureAmount), new BigDecimal(totalHappenedLectureAmount)));
                costOfGradeSubject.setCreateTime(TimeUtils.now());
                costOfGradeSubject.setUpdateTime(TimeUtils.now());
                costOfGradeSubject.setEffectTime(time);
                costOfGradeSubject.setFeeCategoryId(feeCategoryId);
                costOfGradeSubject.setFeeName(feeCategoryId2FeeCategoryOfGradeSubject.get(feeCategoryId).getName());
                costOfGradeSubject.setMainAccountId(mainAccountId);
                costOfGradeSubject.setGradeId(gradeId);
                costOfGradeSubject.setSubjectId(subjectId);
                costOfGradeSubjects.add(costOfGradeSubject);
            }
        }

        return costOfGradeSubjects;
    }

    private List<CostOfGradeSubject> buildSalaryCostOfGradeSubject(Long mainAccountId, Collection<ChangeRecord> changeRecordOfSalaryOfCourse) {
        List<CostOfGradeSubject> costOfGradeSubjects = new ArrayList<>(changeRecordOfSalaryOfCourse.size());

        for (ChangeRecord changeRecord : changeRecordOfSalaryOfCourse) {
            Map<String, String> tradeProp = tradePropService.findByTradeId(changeRecord.getTradeId());
            if (tradeProp.isEmpty() || tradeProp.get("classId") == null ) {
                continue;
            }
            Long classId = Long.valueOf(tradeProp.get("classId"));
            Long classLectureId = Long.valueOf(tradeProp.get("classLectureId"));
            Long teacherId = Long.valueOf(tradeProp.get("teacherId"));
            Integer lectureNo = Integer.valueOf(tradeProp.get("lectureNo"));

            CourseClass courseClass = courseClassService.findById(classId, "gradeId", "subjectId","className","areaTeamName","busTeamName");

            CostOfGradeSubject costOfGradeSubject = new CostOfGradeSubject();
            costOfGradeSubject.setAmount(changeRecord.getAmount());
            costOfGradeSubject.setCreateTime(TimeUtils.now());
            costOfGradeSubject.setUpdateTime(TimeUtils.now());
            costOfGradeSubject.setEffectTime(changeRecord.getEffectTime());
            costOfGradeSubject.setFeeCategoryId(FeeCategoryEnum.SALARY_OF_COURSE.id);
            costOfGradeSubject.setFeeName("课酬");
            costOfGradeSubject.setMainAccountId(mainAccountId);
            costOfGradeSubject.setGradeId(courseClass.getGradeId());
            costOfGradeSubject.setSubjectId(courseClass.getSubjectId());
            Teacher teacher=teacherService.findById(teacherId);
            JSONObject extra = new JSONObject();
            extra.put("classId", classId);
            extra.put("classLectureId", classLectureId);
            if (teacher!=null){
                extra.put("teacherId", teacherId);
                extra.put("teacherName",teacher.getTeacherName());
                extra.put("idNo",teacher.getIdNo());
                extra.put("phone",teacher.getPhoneNo());
            }
            extra.put("lectureNo", lectureNo);
            extra.put("className",courseClass.getClassName());
            extra.put("areaTeamName",courseClass.getAreaTeamName());
            extra.put("busTeamName",courseClass.getBusTeamName());
            costOfGradeSubject.setExtra(extra.toJSONString());
            costOfGradeSubjects.add(costOfGradeSubject);
        }
        return costOfGradeSubjects;
    }

    private void calProductCost(Long mainAccountId, Date time) {

        List<Long> productIds = courseClassService.howManyProductForBusTeam(mainAccountId);
        if (productIds == null || productIds.size() == 0) {
            return;
        }

        //查找这个业务组这一天发生的成本
        Calendar startOfDay = Calendar.getInstance();
        startOfDay.setTime(time);
        startOfDay.set(Calendar.HOUR, 0);
        startOfDay.set(Calendar.MINUTE, 0);
        startOfDay.set(Calendar.SECOND, 0);
        Calendar endOfDay = Calendar.getInstance();
        endOfDay.setTime(time);
        endOfDay.set(Calendar.HOUR, 23);
        endOfDay.set(Calendar.MINUTE, 59);
        endOfDay.set(Calendar.SECOND, 59);
        /*去除租金数据*/
        List<Long> feeCategoryIds  = new ArrayList<>(feeCategoryId2FeeCategoryOfProduct.keySet());
        feeCategoryIds.remove(FeeCategoryEnum.LEAVE.id);
        Example<ChangeRecord> changeRecordExample = ChangeRecord.createExample();
        changeRecordExample.createCriteria().andEqualTo("mainAccountId", mainAccountId)
                .andEqualTo("subject", Subjects.COST)
                .andEqualTo("direction", 0)
                .andIn("categoryId", feeCategoryIds).andBetween("effectTime" , startOfDay.getTime(), endOfDay.getTime() );

        long s  = System.currentTimeMillis();

        List<ChangeRecord> changeRecords = changeRecordService.findListByExample(changeRecordExample);

        XxlJobLogger.log("changeRecordService.findListByExample:" + (System.currentTimeMillis() - s ));

        if (changeRecords.isEmpty()) {
            return;
        }

        //课酬成本
        Collection<ChangeRecord> changeRecordOfSalaryOfCourse = Collections2.filter(changeRecords, new Predicate<ChangeRecord>() {
            @Override
            public boolean apply(ChangeRecord input) {
                return input.getCategoryId().equals(FeeCategoryEnum.SALARY_OF_COURSE.id);
            }
        });

        //其他成本
        List<ChangeRecord> changeRecordOfOther = new ArrayList<>(Collections2.filter(changeRecords, new Predicate<ChangeRecord>() {
            @Override
            public boolean apply(ChangeRecord input) {
                return !input.getCategoryId().equals(FeeCategoryEnum.SALARY_OF_COURSE.id);
            }
        }));

        //清除今天成本(除租金),再重新算


        s  = System.currentTimeMillis();
        clearCostOfProduct(mainAccountId, startOfDay, endOfDay);
        XxlJobLogger.log("clearCostOfProduct:" + (System.currentTimeMillis() - s ));
        List<CostOfProduct> costOfProducts = new ArrayList<>(changeRecordOfSalaryOfCourse.size());

        s  = System.currentTimeMillis();
        costOfProducts.addAll(buildSalaryCostOfProduct(mainAccountId, changeRecordOfSalaryOfCourse));
        XxlJobLogger.log("buildSalaryCostOfProduct:" + (System.currentTimeMillis() - s ));
        s  = System.currentTimeMillis();
        costOfProducts.addAll(buildOtherCostOfProduct(mainAccountId, time, productIds, changeRecordOfOther , startOfDay.getTime(),endOfDay.getTime() ));
        XxlJobLogger.log("buildOtherCostOfProduct:" + (System.currentTimeMillis() - s ));
        if (!costOfProducts.isEmpty()) {
            costOfProductService.insertBatch(costOfProducts);
        }
    }

    private List<CostOfProduct> buildOtherCostOfNowProduct(Long mainAccountId, List<ChangeRecord> changeRecordOfOther , Date time){

        BusTeam busTeam=busTeamService.findById(mainAccountId);
        if (busTeam==null){
            return Collections.EMPTY_LIST;
        }
        Period period=periodService.findPeriodByTime(busTeam.getAreaTeamId(),time);
        if (period==null){
            return Collections.EMPTY_LIST;
        }


        List<Long> productIds=courseClassService.howManyNowProductForBusTeam(mainAccountId,Arrays.asList(0,1,3),period.getPeriodId());

        if (productIds!=null && productIds.size()>0){
            List<CostOfProduct> costOfProducts = new ArrayList<>(productIds.size());
            Map<Long, List<ChangeRecord>> categoryId2changeRecordOfOther = BeanUtils.toGroup(changeRecordOfOther, "categoryId");

            for (Map.Entry<Long, List<ChangeRecord>> changeRecordOfCost : categoryId2changeRecordOfOther.entrySet()) {

                Long feeCategoryId = changeRecordOfCost.getKey();

                BigDecimal totalCost = BigDecimal.ZERO;
                for (ChangeRecord changeRecord : changeRecordOfCost.getValue()) {
                    totalCost = totalCost.add(changeRecord.getAmount());
                }
                for (Long productId:productIds){
                    CostOfProduct costOfProduct = new CostOfProduct();
                    costOfProduct.setAmount(BigDecimals.calProportion(totalCost, new BigDecimal(1), new BigDecimal(productIds.size())));
                    costOfProduct.setCreateTime(TimeUtils.now());
                    costOfProduct.setUpdateTime(TimeUtils.now());
                    costOfProduct.setEffectTime(time);
                    costOfProduct.setFeeCategoryId(feeCategoryId);
                    costOfProduct.setFeeName(feeCategoryId2FeeCategoryOfProduct.get(feeCategoryId).getName());
                    costOfProduct.setMainAccountId(mainAccountId);
                    costOfProduct.setProductId(productId);
                    costOfProducts.add(costOfProduct);
                }

            }
            return costOfProducts;
        }

        return Collections.EMPTY_LIST;
    }

    private List<CostOfProduct> buildOtherCostOfProduct(Long mainAccountId, Date time, List<Long> productIds, List<ChangeRecord> changeRecordOfOther , Date start , Date end ) {
        List<RealIncomeOfClass> realIncomeOfClasses = realIncomeOfClassService.findRealIncomeOfClassByBusTeamIdProductIds(mainAccountId, productIds,start,end );

        if (realIncomeOfClasses.isEmpty()) {
            return buildOtherCostOfNowProduct(mainAccountId,changeRecordOfOther,time);
        }

        List<CostOfProduct> costOfProducts = new ArrayList<>(realIncomeOfClasses.size() * 2);

        Map<Long, Integer> productId2HappenedLectureAmount = new HashMap<>();

        for (RealIncomeOfClass realIncomeOfClass : realIncomeOfClasses) {
            Integer happenedLectureAmount = productId2HappenedLectureAmount.get(realIncomeOfClass.getProductId());
            if (happenedLectureAmount == null) {
                happenedLectureAmount = 0;
            }
            productId2HappenedLectureAmount.put(realIncomeOfClass.getProductId(), happenedLectureAmount + 1);
        }

        Map<Long, List<ChangeRecord>> categoryId2changeRecordOfOther = BeanUtils.toGroup(changeRecordOfOther, "categoryId");

        for (Map.Entry<Long, List<ChangeRecord>> changeRecordOfCost : categoryId2changeRecordOfOther.entrySet()) {

            Long feeCategoryId = changeRecordOfCost.getKey();

            BigDecimal totalCost = BigDecimal.ZERO;
            for (ChangeRecord changeRecord : changeRecordOfCost.getValue()) {
                totalCost = totalCost.add(changeRecord.getAmount());
            }

            int totalHappenedLectureAmount = realIncomeOfClasses.size();
            for (Map.Entry<Long, Integer> entry : productId2HappenedLectureAmount.entrySet()) {
                Long productId = entry.getKey();
                int happenedLectureAmount = entry.getValue();

                CostOfProduct costOfProduct = new CostOfProduct();
                costOfProduct.setAmount(BigDecimals.calProportion(totalCost, new BigDecimal(happenedLectureAmount), new BigDecimal(totalHappenedLectureAmount)));
                costOfProduct.setCreateTime(TimeUtils.now());
                costOfProduct.setUpdateTime(TimeUtils.now());
                costOfProduct.setEffectTime(time);
                costOfProduct.setFeeCategoryId(feeCategoryId);
                costOfProduct.setFeeName(feeCategoryId2FeeCategoryOfProduct.get(feeCategoryId).getName());
                costOfProduct.setMainAccountId(mainAccountId);
                costOfProduct.setProductId(productId);
                costOfProducts.add(costOfProduct);
            }
        }
        return costOfProducts;
    }

    private List<CostOfProduct> buildSalaryCostOfProduct(Long mainAccountId, Collection<ChangeRecord> changeRecordOfSalaryOfCourse) {
        List<CostOfProduct> costOfProducts = new ArrayList<>(changeRecordOfSalaryOfCourse.size());
        for (ChangeRecord changeRecord : changeRecordOfSalaryOfCourse) {
            Map<String, String> tradeProp = tradePropService.findByTradeId(changeRecord.getTradeId());
            if (tradeProp.isEmpty() || tradeProp.get("classId") == null ) {
                continue;
            }
            Long classId = Long.valueOf(tradeProp.get("classId"));
            Long classLectureId = Long.valueOf(tradeProp.get("classLectureId"));
            Long teacherId = Long.valueOf(tradeProp.get("teacherId"));
            Integer lectureNo = Integer.valueOf(tradeProp.get("lectureNo"));

            CourseClass courseClass = courseClassService.findById(classId, "productId","className","areaTeamName","busTeamName");
            Teacher teacher=teacherService.findById(teacherId);
            CostOfProduct costOfProduct = new CostOfProduct();
            costOfProduct.setAmount(changeRecord.getAmount());
            costOfProduct.setCreateTime(TimeUtils.now());
            costOfProduct.setUpdateTime(TimeUtils.now());
            costOfProduct.setEffectTime(changeRecord.getEffectTime());
            costOfProduct.setFeeCategoryId(FeeCategoryEnum.SALARY_OF_COURSE.id);
            costOfProduct.setFeeName("课酬");
            costOfProduct.setMainAccountId(mainAccountId);
            costOfProduct.setProductId(courseClass.getProductId());

            JSONObject extra = new JSONObject();
            extra.put("classId", classId);
            extra.put("classLectureId", classLectureId);

            if (teacher!=null){
                extra.put("teacherId", teacherId);
                extra.put("teacherName",teacher.getTeacherName());
                extra.put("idNo",teacher.getIdNo());
                extra.put("phone",teacher.getPhoneNo());
            }
            extra.put("lectureNo", lectureNo);
            extra.put("className",courseClass.getClassName());
            extra.put("areaTeamName",courseClass.getAreaTeamName());
            extra.put("busTeamName",courseClass.getBusTeamName());
            costOfProduct.setExtra(extra.toJSONString());

            costOfProducts.add(costOfProduct);
        }
        return costOfProducts;
    }

    private void addProductLease(ClassLecture classLecture, ChangeRecord changeRecord, CourseClass courseClass){
        costOfProductService.deleteById(classLecture.getClassLectureId());
        BigDecimal leaseAmount=this.calLeaveOfClassLecture(Arrays.asList(classLecture));
        CostOfProduct costOfProduct = new CostOfProduct();
        costOfProduct.setCostOfProductId(classLecture.getClassLectureId());
        costOfProduct.setAmount(leaseAmount);
        costOfProduct.setCreateTime(TimeUtils.now());
        costOfProduct.setUpdateTime(TimeUtils.now());
        costOfProduct.setEffectTime(changeRecord.getEffectTime());
        costOfProduct.setFeeCategoryId(FeeCategoryEnum.LEAVE.id);
        costOfProduct.setFeeName("租金");
        costOfProduct.setMainAccountId(changeRecord.getMainAccountId());
        costOfProduct.setProductId(courseClass.getProductId());

     /*   JSONObject extra = new JSONObject();
        extra.put("classId", courseClass.getClassId());
        extra.put("classLectureId", classLecture.getClassLectureId());
        extra.put("lectureNo", classLecture.getLectureNo());
        costOfProduct.setExtra(extra.toJSONString());*/
        costOfProductService.insert(costOfProduct);
    }

    private void addGradeSubjectLease(ClassLecture classLecture,ChangeRecord changeRecord,CourseClass courseClass){
        costOfGradeSubjectService.deleteById(classLecture.getClassLectureId());
        BigDecimal amount=this.calLeaveOfClassLecture(Arrays.asList(classLecture));
        CostOfGradeSubject costOfGradeSubject = new CostOfGradeSubject();
        costOfGradeSubject.setCostOfGradeSubjectId(classLecture.getClassLectureId());
        costOfGradeSubject.setAmount(amount);
        costOfGradeSubject.setCreateTime(TimeUtils.now());
        costOfGradeSubject.setUpdateTime(TimeUtils.now());
        costOfGradeSubject.setEffectTime(changeRecord.getEffectTime());
        costOfGradeSubject.setFeeCategoryId(FeeCategoryEnum.LEAVE.id);
        costOfGradeSubject.setFeeName("租金");
        costOfGradeSubject.setMainAccountId(changeRecord.getMainAccountId());
        costOfGradeSubject.setGradeId(courseClass.getGradeId());
        costOfGradeSubject.setSubjectId(courseClass.getSubjectId());
        costOfGradeSubjectService.insert(costOfGradeSubject);
    }

    private void clearCostOfGradeSubject(Long mainAccountId, Calendar startOfDay, Calendar endOfDay) {
        Example<CostOfGradeSubject> costOfGradeSubjectExample = CostOfGradeSubject.createExample();
        costOfGradeSubjectExample.createCriteria().andEqualTo("mainAccountId", mainAccountId).andBetween("effectTime", startOfDay.getTime(), endOfDay.getTime()).andNotEqualTo("feeCategoryId", FeeCategoryEnum.LEAVE.id);
        costOfGradeSubjectService.deleteByExample(costOfGradeSubjectExample);
    }

    private void clearCostOfProduct(Long mainAccountId, Calendar startOfDay, Calendar endOfDay) {
        Example<CostOfProduct> costOfProductExample = CostOfProduct.createExample();
        costOfProductExample.createCriteria().andEqualTo("mainAccountId", mainAccountId).andBetween("effectTime", startOfDay.getTime(), endOfDay.getTime()).andNotEqualTo("feeCategoryId", FeeCategoryEnum.LEAVE.id);
        costOfProductService.deleteByExample(costOfProductExample);
    }

    private void saveRealIncomeOfCourseClass(ChangeRecord changeRecord, CourseClass courseClass, Student student, CourseTemplate courseTemplate, BigDecimal amount, Long classLectureId) {
        RealIncomeOfClass realIncomeOfClass = new RealIncomeOfClass();
        realIncomeOfClass.setRealIncomeOfClassId(changeRecord.getChangeRecordId() + "|" + classLectureId);
        realIncomeOfClass.setAreaTeamId(courseClass.getAreaTeamId());
        realIncomeOfClass.setBusTeamId(courseClass.getBusTeamId());
        realIncomeOfClass.setClassId(courseClass.getClassId());
        realIncomeOfClass.setClassName(courseClass.getClassName());
        realIncomeOfClass.setGradeId(courseClass.getGradeId());
        realIncomeOfClass.setGradeName(courseClass.getGradeName());
        realIncomeOfClass.setSubjectId(courseClass.getSubjectId());
        realIncomeOfClass.setSubjectName(courseClass.getSubjectName());
        realIncomeOfClass.setProductId(courseTemplate.getProductId());
        realIncomeOfClass.setProductName(courseTemplate.getProductName());
        realIncomeOfClass.setStudentId(student.getStudentId());
        realIncomeOfClass.setStudentName(student.getName());
        realIncomeOfClass.setAmount(amount);
        realIncomeOfClass.setLectureId(classLectureId);
        realIncomeOfClass.setCreateTime(TimeUtils.now());
        realIncomeOfClass.setUpdateTime(TimeUtils.now());
        realIncomeOfClass.setEffectTime(changeRecord.getEffectTime());
        try {
            realIncomeOfClassService.insertSelective(realIncomeOfClass);
        } catch (Exception e) {
            realIncomeOfClass.setCreateTime(null);
            realIncomeOfClassService.updateSelectiveById(realIncomeOfClass);
        }
    }


    private void buildRealIncomeOfCourseClass(ChangeRecord changeRecord) {

        Map<String, String> tradeProp = tradePropService.findByTradeId(changeRecord.getTradeId());

        if (tradeProp.isEmpty()) {
            return;
        }

        Long classId = Long.valueOf(tradeProp.get("classId"));
        Long studentId = Long.valueOf(tradeProp.get("studentId"));

        CourseClass courseClass = courseClassService.findById(classId);
        Student student = studentService.findById(studentId);

        if(student == null ){
            StudentReg studentReg = studentRegService.findById(changeRecord.getTrackingId(), "studentId");
            if(studentReg == null ){
                return  ;
            }
            student = studentService.findById(studentReg.getStudentId());

            if(student == null ){
                return;
            }
        }

        CourseTemplate courseTemplate = courseTemplateService.findById(courseClass.getCourseTemplateId());

        List<Long> classLectureIds = null;

        if (tradeProp.containsKey("refundClassLectureIds")) {//退费的
            classLectureIds = Utils.toLongIds(tradeProp.get("refundClassLectureIds"));
        }else if(tradeProp.containsKey("differenceClassLectureIds")){//差额营收
            classLectureIds = Utils.toLongIds(tradeProp.get("differenceClassLectureIds"));
        } else {
            Long classLectureId=Long.valueOf(tradeProp.get("classLectureId"));
            classLectureIds = Arrays.asList(classLectureId);
            ClassLecture classLecture=classLectureService.findById(classLectureId);
            if (classLecture!=null){
                this.addProductLease(classLecture,changeRecord,courseClass);
                this.addGradeSubjectLease(classLecture,changeRecord,courseClass);
            }

        }





        BigDecimal amount = BigDecimals.calProportion(changeRecord.getAmount(), BigDecimal.ONE, BigDecimal.valueOf(classLectureIds.size()));

        for (Long classLectureId : classLectureIds) {
            saveRealIncomeOfCourseClass(changeRecord, courseClass, student, courseTemplate, amount, classLectureId);
        }

    }


    public BigDecimal calLeaveOfClassLecture(List<ClassLecture> classLectureList){
        BigDecimal lectureAmount=BigDecimal.ZERO;
        if (classLectureList==null || classLectureList.size()<=0 ){
            return lectureAmount;
        }
        List<Long> classIds=BeanUtils.toList(classLectureList,"classId");
        List<CourseClass> courseClassList=courseClassService.findByIds(classIds,"classId","roomId");
        Set<Long> roomIdSet=new HashSet<Long>();
        for (CourseClass courseClass:courseClassList){
            roomIdSet.add(courseClass.getRoomId());
        }
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy");
        Set<String> classDateSet=new HashSet<String>();
        for (ClassLecture classLecture:classLectureList){
            if (classLecture.getClassDate()!=null){
                classDateSet.add(simpleDateFormat.format(classLecture.getClassDate()));
            }
        }
        List<RoomPrice> roomPriceList=roomPriceService.findByYearsAndRoomIds(classDateSet,roomIdSet);
        Map<Long,CourseClass> courseClassMap=BeanUtils.toMap(courseClassList,"classId");
        if (roomPriceList==null || roomPriceList.size()<=0){
            return lectureAmount;
        }
        for (ClassLecture classLecture:classLectureList){
            if (courseClassMap.containsKey(classLecture.getClassId())){
                String year=simpleDateFormat.format(classLecture.getClassDate());
                Long roomId=courseClassMap.get(classLecture.getClassId()).getRoomId();
                for (RoomPrice roomPrice:roomPriceList){
                    if (roomPrice.getYear().equals(year) && roomPrice.getRoomId().equals(roomId)){
                        Long classTime=classLecture.getClassDate().getTime();
                        int weekNum= TimeUtils.getWeekOfDate(classLecture.getClassDate());
                        if (classTime>=roomPrice.getSummerStartDate().getTime() && classTime<=roomPrice.getSummerEndDate().getTime()){//暑假
                            lectureAmount=lectureAmount.add(roomPrice.getSummerCost().multiply(new BigDecimal(2)));
                            break;
                        }else if (classTime>=roomPrice.getWinterStartDate().getTime() && classTime<=roomPrice.getWinterEndDate().getTime()){//寒假
                            lectureAmount=lectureAmount.add(roomPrice.getWinterCost().multiply(new BigDecimal(2)));
                            break;
                        }else if (weekNum==0 || weekNum==6){//周末
                            lectureAmount=lectureAmount.add(roomPrice.getWeekendCost().multiply(new BigDecimal(2)));
                            break;
                        }else {
                            lectureAmount=lectureAmount.add(roomPrice.getWorkingCost().multiply(new BigDecimal(2)));
                            break;
                        }
                    }
                }

            }
        }
        return lectureAmount;
    }

}
